package csm.marc;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.jdom.Element;

import csm.common.*;

/**
 * 이 클래스는 MARC의 태그정보를 관리한다.
 * 
 * @author seungmun
 * @since 2006.1.18.
 */
public class Tag implements Comparable<Tag>
{
	private int tag;
	private char indi1;
	private char indi2;
	private String data;
	private boolean isControlTag;
	private ArrayList<SubField> subfield = new ArrayList<SubField>();
	private String checkKey;

	/**
	 * 생성자 
	 * @param tag
	 */
	public Tag(int tag)
	{
		this.tag = tag;
		this.data = "";

		if (tag < 10)
		{
			isControlTag = true;
			if (this.data.endsWith((char) Delimiter.TAG + "")) 
				this.data = this.data.substring(0, data.length() - 1);

			if (this.data.endsWith((char) Delimiter.SUBFIELD + "")) 
				this.data = this.data.substring(0, data.length() - 1);

			if (this.data.endsWith("&")) 
				this.data = this.data.substring(0, data.length() - 1);
		}
		else
		{
			isControlTag = false;
			indi1 = ' ';
			indi2 = ' ';
			parse();
		}
	}

	/**
	 * 생성자 
	 * @param tag
	 * @param indi
	 */
	public Tag(int tag, String indi)
	{
		this.tag = tag;
		this.data = "";

		if (tag < 10)
		{
			isControlTag = true;
			if (this.data.endsWith((char) Delimiter.TAG + "")) 
				this.data = this.data.substring(0, data.length() - 1);

			if (this.data.endsWith((char) Delimiter.SUBFIELD + "")) 
				this.data = this.data.substring(0, data.length() - 1);

			if (this.data.endsWith("&")) 
				this.data = this.data.substring(0, data.length() - 1);
		}
		else
		{
			isControlTag = false;

			if (StringUtil.isNullOrEmpty(indi))
			{
				indi1 = ' ';
				indi2 = ' ';
			}
			else
			{
				indi1 = indi.charAt(0);
				indi2 = ' ';
				if (indi.length() > 1) indi2 = indi.charAt(1);
			}
			parse();
		}
	}

	/**
	 * 생성자
	 * 
	 * @param tag 태그번호
	 * @param indi 지시기호
	 * @param data
	 *            {@link SubField}를 자동을 생성하기 위한 문자열 데이터 입니다.
	 * <blockquote>
	 * <pre>
	 * <i>[subfield delemiter][subfield cdoe][subfield data]</i>의 형식을 갖춘 문자열이어야 서브필드가 자동으로 생성됩니다.
	 * <b>subfield delimiter</b>로는 '$' 문자나, \u001f 문자, {@link Delimiter}.SUBFIELD 열거형을 이용할 수 있습니다.
	 * 
	 * <b>&lt;주의&gt;</b> 
	 *    <i>한 문자열 데이터내에는 한 종류의 subfield delimiter를 사용하십시요.</i>
	 * 
	 * &lt;Example&gt;
	 *    Tag t = new Tag( 245, ' ', "$a자료내용: $b자료내용");
	 * </pre>
	 * </blockquote>            
	 */
	public Tag(int tag, String indi, String data)
	{
		this.tag = tag;
		this.data = data;

		if (tag < 10)
		{
			isControlTag = true;
			if (this.data.endsWith((char) Delimiter.TAG + "")) 
				this.data = this.data.substring(0, data.length() - 1);

			if (this.data.endsWith((char) Delimiter.SUBFIELD + "")) 
				this.data = this.data.substring(0, data.length() - 1);

			if (this.data.endsWith("&")) 
				this.data = this.data.substring(0, data.length() - 1);
		}
		else
		{
			isControlTag = false;
			if (StringUtil.isNullOrEmpty(indi))
			{
				indi1 = ' ';
				indi2 = ' ';
			}
			else
			{
				indi1 = indi.charAt(0);
				indi2 = ' ';
				if (indi.length() > 1) indi2 = indi.charAt(1);
			}
			parse();
		}
	}

	/**
	 * 생성자 
	 * @param tag
	 * @param ind1
	 * @param ind2
	 * @param xmlNodelist
	 */
	public Tag(int tag, char ind1, char ind2, List<Element> xmlNodelist)
	{
		this.tag = tag;
		this.indi1 = ind1;
		this.indi2 = ind2;

		if (tag < 10)
			isControlTag = true;
		else
			isControlTag = false;

		if (xmlNodelist == null) return;

		Element node = null;
		String xmlText = null;
		SubField sf = null;
		        
		for (int i = 0; i < xmlNodelist.size(); i++)
		{
			node = (Element) xmlNodelist.get(i);

			if (isControlTag)
			{
				this.data = node.getText();
				return;
			}

			xmlText = node.getAttributeValue("code");

			if (!StringUtil.isNullOrEmpty(xmlText))
			{
				char code = xmlText.charAt(0);

				xmlText = node.getText();

				sf = new SubField(code, xmlText);

				subfield.add(sf);
			}
		}
	}

	/**
	 * 데이타 필드인 경우는 서브필드를 파싱해서 Hashtable에 넣는다
	 */
	private void parse()
	{
		if (isControlTag) return;

		String[] token = data.split(Delimiter.SUBFIELD + "");

		if (token.length == 1 && data.startsWith("$"))
		{
			data = data.replace('$', Delimiter.SUBFIELD);
			token = data.split(Delimiter.SUBFIELD + "");
		}

		SubField sf = null;
		for (int i = 0; i < token.length; i++)
		{
			String subfData = token[i];

			if ( !StringUtil.isNullOrEmpty(subfData))
			{
				char code = subfData.charAt(0);

				if (!(code >= '0' && code <= '9') && !(code >= 'a' && code <= 'z')) continue;

				subfData = subfData.substring(0, subfData.length());
				sf = new SubField(code, subfData.length() > 1 ? subfData.substring(1) : "");
				subfield.add(sf);
			}
		}
	}

	/**
	 * 서브필드의 갯수를 리턴한다.
	 * 
	 * @return 서브필드 갯수
	 */
	public int getSubFieldCount()
	{
		if (isControlTag) return 0;
		return subfield.size();
	}

	/**
	 * 태그의 offset번째 서브필드의 코드를 리턴한다
	 * 
	 * @param offset
	 *            몇번째 위치정보
	 * @return 코드
	 */
    public char getSubFieldCode(int offset)
    {
        SubField sf = (SubField) subfield.get(offset);
        return sf.getCode();
    }

	/**
	 * 태그의 offset번째 서브필드의 데이타를 리턴한다.
	 * 
	 * @param offset
	 *            몇번째 위치정보
	 * @return 서브필드데이타
	 */
    public String getSubFieldData(int offset)
    {
        SubField sf = subfield.get(offset);
        return sf.getData();
    }

	/**
	 * 태그안에 해당 서브필드를 String 배열로 리턴(서브필드 기호는 포함되지 않는다)
	 * 
	 * @param code
	 *            찾을 서브필드코드
	 * @param returnCount
	 *            리턴받을 배열의 최대 건수, 0이면 무한대
	 * @return String[] 서브필드값
	 */
    public String[] getSubFieldData(char code, int returnCount)
    {
        ArrayList<String> retval = new ArrayList<String>();
        int siz = subfield.size();
        SubField sf = null;
        for (int i = 0, count = 1; i < siz ; i++)
        {
            sf = subfield.get(i);
            if (sf.getCode() == code)
            {
                retval.add(sf.getData());
                if (count++ == returnCount)
                    break;
            }
        }
        return (String[]) retval.toArray(new String[retval.size()]);
    }

	/**
	 * 태그안에 해당 서브필드를 리턴
	 * 
	 * @param code
	 *            찾을 서브필드코드
	 * @return 서브필드값
	 * @throws Exception
	 */
	public String getSubFieldData(char code)
    {
	    SubField sf = null;
        for (int i = 0; i < subfield.size(); i++)
        {
            sf = subfield.get(i);

            if (sf.getCode() == code)
                return sf.getData();
        }
        return "";
	}

	/**
	 * 전체 서브필드를 리턴한다
	 * 
	 * @return 서브필드를 iterator 형태로 리턴
	 */
	public SubField[] getSubFields()
	{
		return subfield.toArray((new SubField[subfield.size()]));
	}

	/**
	 * 전체 서브필드를 리턴한다
	 * 
	 * @return
	 */
	public ArrayList<SubField> getSubFieldList()
	{
		return this.subfield;
	}

	/**
	 * 지시기호를 가져옵니다.
	 * @return Returns the indi.
	 */
	public String getIndi()
	{
		if (isControlTag) return "  ";
		char[] indi = new char[2];
		indi[0] = indi1;
		indi[1] = indi2;

		return String.valueOf(indi);
	}

	/**
	 * 태그번호를 가져옵니다.
	 * @return Returns the tag.
	 */
	public int getTagNo()
	{
		return tag;
	}

	/**
	 * 태그번호가 010 이하인 컨트롤 태그인가?
	 * 
	 * @return Returns the isControlTag.
	 */
	public boolean isControlTag()
	{
		return isControlTag;
	}

	/**
	 * 구분기호(태그,서브필드)를 포함한 현재 태그의 데이터를 가져옵니다.
	 * 
	 * @return
	 */
	public String getTagData()
	{
		return getTagData(true);
	}

	/**
	 * 현재 태그의 데이터를 가져옵니다.
	 * 
	 * @param includeTagDelimiter
	 *            구분기호(태그,서브필드)를 포함시킬지 여부.
	 * @return
	 */
	public String getTagData(boolean includeTagDelimiter)
	{
		if (!isControlTag)
		{
			StringBuffer sb = new StringBuffer();
			sb.append(indi1);
			sb.append(indi2);

			SubField sf = null;
			Iterator<SubField> it = subfield.iterator();
			while (it.hasNext())
			{
				sf = it.next();
				sb.append(Delimiter.SUBFIELD).append(sf.getCode()).append(sf.getData());
			}
			sb.append(Delimiter.TAG);
			return sb.toString();
		}
		else
		{
			if (includeTagDelimiter)
			{
				if (data.indexOf(Delimiter.TAG) < 0)
					return data + Delimiter.TAG;
				else
					return data;
			}
			else
			{
				int index = 0;
				if ((index = data.indexOf(Delimiter.TAG)) >= 0)
					return data.substring(0, index);
				else
					return data;
			}
		}
	}

	/**
	 * 이 태그를 유일하게 구분할 수 있는 값을 설정합니다.
	 * 
	 * @param key
	 */
	public void setCheckKey(String key)
	{
		this.checkKey = key;
	}

	/**
	 * 이 태그를 유일하게 구분할 수 있는 값을 가져옵니다.
	 * 
	 * @return
	 */
	public String getCheckKey()
	{
		return this.checkKey;
	}

	public int compareTo(Tag o)
	{
	    return this.checkKey.compareTo(o.getCheckKey());
	}

	public boolean equals(Object obj)
	{
		if (obj instanceof Tag)
			return this.checkKey.equals(((Tag) obj).getCheckKey());
		else if (obj instanceof String)
			return this.checkKey.equals(obj);
		else
			return false;
	}

	/**
	 * SubField를 추가한다.
	 * 
	 * @param code
	 *            char 서브필드코드
	 * @param data
	 *            String 서브필드데이터
	 */
	public void addSubField(char code, String data)
	{
		subfield.add(new SubField(code, data));
	}

	public static void main(String[] args) throws Exception, SMException
	{
	    Tag t = new Tag(100, "1 ", "$a서울 :$a東國大學校 大學院, $a1976");
		//Tag t = new Tag(100, "1 ", "a서울 :a東國大學校 大學院,a1976. c9999.");
		// Tag t = new Tag(100, "1 ", "서울 :東國大學校 大學院,c1976.");
		
		System.out.println("getSubFieldCount:" + t.getSubFieldCount());
		System.out.println("getSubFieldCode:" + t.getSubFieldCode(0));
		
		String[] a = t.getSubFieldData('a', 0);
		for (int i = 0; i < a.length; i++){
			System.out.println("getSubFieldData:" + a[i]);
		}
		
		System.out.println("getSubFieldData:" + t.getSubFieldData('a'));
		
		System.out.println( "---------------");
		SubField[] s = t.getSubFields();
		//for (int i = s.length - 1; i >= 0; i--)
		for( int i = 0; i< s.length; i++)
		{
		    System.out.println( MessageFormat.format("{0}{1}", s[i].getCode(), s[i].getData()));
		}
		
		System.out.println( "---------------");
		List<SubField> buff = t.getSubFieldList();
		for( SubField sf : buff)
		{
            System.out.println( MessageFormat.format("{0}{1}", sf.getCode(), sf.getData()));
		}
	}
}
